#pragma once

#include <set>
#include <vector>
#include <iostream>
#include <tuple>
#include <map>

using namespace std;

// Grafo implementado utilizando conjuntos
template<typename V = char, typename E = char>
class AltSetGraph
{
  map<int,V> _nodes;

  map<tuple<int,int>,E> _edges;

  tuple<int,int> makePair(int node1, int node2) const
  {
    if (node1 > node2) {
      return make_tuple(node1, node2);
    }
    
    return make_tuple(node2, node1);
  }

public:
  int addNode(const V &value = V())
  {
    int id = _nodes.size();
    _nodes[id] = value;
    return id;
  }

  const V &getNodeValue(int n) const
  {
    return _nodes.at(n);
  }

  int countNodes() const
  {
    return _nodes.size();
  }

  int getNodeDegree(int node) const
  {
    int d = 0;
    for (auto i: _edges) {
      if (get<0>(i.first) == node || get<1>(i.first) == node) {
        d++;
      }
    }
    return d;
  }

  int countEdges() const
  {
    return _edges.size();
  }

  bool addEdge(int node1, int node2, const E &value = E())
  {
    // verifica se os vértices existem
    if (node1 == node2 || _nodes.find(node1) == _nodes.end() || _nodes.find(node2) == _nodes.end()) {
      return false;
    }

    _edges[makePair(node1, node2)] = value;

    return true;
  }
  
  bool areNodesAdjacents(int node1, int node2)
  {
    return _edges.find(makePair(node1,node2)) != _edges.end();
  }
  
  const E &getEdgeValue(int node1, int node2) const
  {
    return _edges.find(makePair(node1,node2))->second;
  }

  vector<int> getAdjacentNodes(int node) const
  {
    vector<int> a;

    for (auto edge: _edges) {
      if (get<0>(edge.first) == node) {
        a.push_back(get<1>(edge.first));
      } else if (get<1>(edge.first) == node) {
        a.push_back(get<0>(edge.first));
      }
    } 

    return a;
  }
};
